package com.yuntian.baidu;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.yuntian.baidu.YthBdpApiExecuteClient.postRequestByCondition;

/**
 * 永拓人工智能项目 API 工具类（基于BCE签名改造）
 * 包含：组织机构接口（basicOrg）、员工数据接口（basicEmp）
 */
@Slf4j
@Component
public class YthService {

    // ====================== 1. 接口常量配置 ======================
    /**
     * 组织机构接口地址
     */
    private static final String BASIC_ORG_URL = "http://gwgp-wcjmjlpkro4.i.apiverse.ythit.cn/ythAI/basicData/orgUnit/v1";
    /**
     * 员工数据接口地址
     */
    private static final String BASIC_EMP_URL = "http://gwgp-wcjmjlpkro4.i.apiverse.ythit.cn/ythAI/basicData/empInfo/v1";
    /**
     * Content-Type
     */
    private static final String CONTENT_TYPE = "application/x-www-form-urlencoded;charset=utf-8";
    /**
     * 默认组织ID
     */
    private static final String DEFAULT_ORG_ID = "-1730833917365171641";
    /**
     * 最大每页条数
     */
    private static final int MAX_PAGE_SIZE = 1000;
    /**
     * 默认每页条数
     */
    private static final int DEFAULT_PAGE_SIZE = 10;

    // ====================== 2. BCE签名常量 ======================
    private static final String BCE_AUTH_VERSION = "bce-auth-v1";
    private static final String BCE_PREFIX_LOWER_CASE = "x-bce-";
    private static final String SIGNATURE_HEADER_NAME = "X-Bce-Signature";
    private static final Set<String> DEFAULT_HEADERS_TO_SIGN = new HashSet<>(Arrays.asList(
            "host", "content-length", "content-type", "content-md5"));
    private static final DateTimeFormatter DATE_TIME_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
                    .withZone(ZoneOffset.UTC);
    private static final String HEADER_JOINER = "\n";
    private static final String QUERY_STRING_JOINER = "&";
    private static final String SIGNED_HEADER_STRING_JOINER = ";";

    // ====================== 3. 授权信息配置 ======================
    @Value("${yth.userCode:YTH_AI}")
    private String userCode;

    @Value("${yth.authCode:wIxyNE9O0btbGiRAWnjM2Q==}")
    private String authCode;

    @Value("${yth.accessKey:65e9bffe164f4b9b831da45524b664ad}")
    private String bceAccessKey;

    @Value("${yth.secretKey:ed4b0a37520f453e920bb98501f3eaf4}")
    private String bceSecretKey;

    // ====================== 4. SSL初始化 ======================
    static {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new TrustManager[]{new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {return null;}

                public void checkClientTrusted(X509Certificate[] certs, String authType) {}

                public void checkServerTrusted(X509Certificate[] certs, String authType) {}
            }}, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
        } catch (Exception e) {
            throw new RuntimeException("SSL初始化失败", e);
        }
    }

    // ====================== 5. 业务方法 ======================

    /**
     * 获取全部组织机构数据（自动分页）
     */
    public List<OrgData> getAllOrgData() {
        List<OrgData> allOrgList = CollUtil.newArrayList();
        int pageNum = 1;
        while (true) {
            YthPageResponse<OrgData> pageData = this.getOrgPageData(pageNum, MAX_PAGE_SIZE);
            YthPageResponse.PageResult<OrgData> pageResult = pageData.getResult();
            allOrgList.addAll(pageResult.getResult());
            if (pageResult.getResult().size() < pageResult.getPageSize()
                    || pageNum >= (pageResult.getTotalNum() + pageResult.getPageSize() - 1) / pageResult.getPageSize()) {
                break;
            }
            pageNum++;
        }
        return allOrgList;
    }

    /**
     * 获取全部员工数据（自动分页）
     */
    public List<EmpData> getAllEmpData() {
        List<EmpData> allEmpList = CollUtil.newArrayList();
        int pageNum = 1;
        while (true) {
            YthPageResponse<EmpData> pageData = this.getEmpPageData(pageNum, MAX_PAGE_SIZE);
            YthPageResponse.PageResult<EmpData> pageResult = pageData.getResult();
            allEmpList.addAll(pageResult.getResult());

            if (pageResult.getResult().size() < pageResult.getPageSize()
                    || pageNum >= (pageResult.getTotalNum() + pageResult.getPageSize() - 1) / pageResult.getPageSize()) {
                break;
            }
            pageNum++;
        }
        return allEmpList;
    }

    /**
     * 调用组织机构接口获取分页数据
     */
    public YthPageResponse<OrgData> getOrgPageData(Integer pageNum, Integer pageSize) {
        // 应用 appKey
        String accessKey = "65e9bffe164f4b9b831da45524b664ad";
        // 应用 appSecret
        String secretKey = "ed4b0a37520f453e920bb98501f3eaf4";

        // POST请求地址
        String url = "http://gwgp-wcjmjlpkro4.i.apiverse.ythit.cn/ythAI/basicData/orgUnit/v1";
        // POST请求参数
        String param = StrUtil.format("pageSize={}&pageNum={}&userCode={}&authCode={}", pageSize, pageNum, userCode, authCode);
        // POST请求Content-Type
        String contentType = "application/x-www-form-urlencoded;charset=utf-8";
        // POST方式请求
        try {
            String s = postRequestByCondition(url,contentType,param,accessKey, secretKey);
            return parseResponse(s, pageSize, pageNum, new TypeReference<YthPageResponse<OrgData>>() {});
        } catch (IOException e) {
            log.error("query  org error",e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 调用员工数据接口获取分页数据
     */
    public YthPageResponse<EmpData> getEmpPageData(Integer pageNum, Integer pageSize ) {
        // 应用 appKey
        String accessKey = "65e9bffe164f4b9b831da45524b664ad";
        // 应用 appSecret
        String secretKey = "ed4b0a37520f453e920bb98501f3eaf4";

        // POST请求地址
        String url = "http://gwgp-wcjmjlpkro4.i.apiverse.ythit.cn/ythAI/basicData/empInfo/v1";
        // POST请求参数
        String param = StrUtil.format("pageSize={}&pageNum={}&userCode={}&authCode={}", pageSize, pageNum, userCode, authCode);
        // POST请求Content-Type
        String contentType = "application/x-www-form-urlencoded;charset=utf-8";
        // POST方式请求
        try {
            String s = postRequestByCondition(url,contentType,param, accessKey, secretKey);
            return parseResponse(s, pageSize, pageNum, new TypeReference<YthPageResponse<EmpData>>() {});
        } catch (IOException e) {
            log.error("query  org error",e.getMessage());
            throw new RuntimeException(e);
        }
    }

    // ====================== 6. 工具方法 ======================

    /**
     * 构建参数字符串
     */
    private String buildParamString(Object paramObject) {
        return BeanUtil.beanToMap(paramObject)
                .entrySet().stream()
                .filter(entry -> entry.getValue() != null)
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining("&"));
    }

    /**
     * 解析响应数据
     */
    private <T> YthPageResponse<T> parseResponse(String responseBody, int pageSize, int pageNum, TypeReference<YthPageResponse<T>> typeReference) {
        if (StrUtil.isBlank(responseBody)) {
            throw new RuntimeException("接口返回空响应");
        }

        YthPageResponse<T> pageResponse = JSONUtil.toBean(responseBody, typeReference, false);

        // 补全空数据
        if (pageResponse.getResult() == null) {
            pageResponse.setResult(new YthPageResponse.PageResult<>(pageSize, pageNum, 0, CollUtil.newArrayList()));
        } else if (pageResponse.getResult().getResult() == null) {
            pageResponse.getResult().setResult(CollUtil.newArrayList());
        }

        return pageResponse;
    }

    /**
     * 发送带BCE签名的POST请求
     */
    private String sendSignedPostRequest(String url, String paramStr) throws IOException {
        Credential credential = new Credential(bceAccessKey, bceSecretKey);
        String httpMethod = "POST";
        URI uri = URI.create(url);
        Map<String, String> headers = new HashMap<>();

        // 设置基础Header
        headers.put("Host", uri.getHost());
        headers.put("Content-Type", CONTENT_TYPE);

        // 生成签名
        sign(httpMethod, uri, headers, credential, null);

        // 发送请求
        URL requestUrl = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
        try {
            connection.setRequestMethod(httpMethod);
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(10000);
            connection.setInstanceFollowRedirects(true);

            // 设置签名Header
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                connection.setRequestProperty(entry.getKey(), entry.getValue());
            }

            // 处理POST请求体
            connection.setDoOutput(true);
            try (OutputStream outputStream = connection.getOutputStream()) {
                outputStream.write(paramStr.getBytes(StandardCharsets.UTF_8));
                outputStream.flush();
            }

            // 处理响应
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                try (BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                    return in.lines().collect(Collectors.joining());
                }
            } else {
                // 读取错误信息
                if (ObjUtil.isNotEmpty(connection.getErrorStream())) {
                    try (BufferedReader errorIn = new BufferedReader(
                            new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8))) {
                        String errorMsg = errorIn.lines().collect(Collectors.joining());
                        throw new IOException(String.format("请求失败，状态码：%d，错误信息：%s", responseCode, errorMsg));
                    }
                }

            }
        } finally {
            connection.disconnect();
        }
        return null;
    }

    /**
     * BCE签名生成方法
     */
    private void sign(String method, URI uri, Map<String, String> headers, Credential credential, SignOptions options) {
        Map<String, String> httpHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        httpHeaders.putAll(headers);
        Objects.requireNonNull(httpHeaders.get("host"), "Host header is required.");

        if (options == null) {
            options = SignOptions.DEFAULT;
        }

        String timestamp = options.getTimestamp();
        if (timestamp == null) {
            timestamp = DATE_TIME_FORMATTER.format(Instant.now());
        }

        // 生成authString
        String authString = BCE_AUTH_VERSION + "/" + credential.getAccessKey() + "/"
                + timestamp + "/" + options.getExpirationInSeconds();

        // 生成签名密钥
        String signingKey = SignUtils.sha256Hex(credential.getSecretKey(), authString);

        // 生成规范URI、查询字符串、Header
        String canonicalURI = getCanonicalURIPath(uri.getPath());
        String canonicalQueryString = getCanonicalQueryString(uri.getRawQuery());
        SortedMap<String, String> headersToSign = getHeadersToSign(httpHeaders, options.getHeadersToSign());
        String canonicalHeader = getCanonicalHeaders(headersToSign);
        String signedHeaders = "";
        if (options.getHeadersToSign() != null) {
            signedHeaders = SignUtils.join(SIGNED_HEADER_STRING_JOINER, headersToSign.keySet());
            signedHeaders = signedHeaders.trim().toLowerCase();
        }

        // 生成规范请求串
        String canonicalRequest = method.toUpperCase() + HEADER_JOINER + canonicalURI + HEADER_JOINER
                + canonicalQueryString + HEADER_JOINER + canonicalHeader;

        // 生成签名
        String signature = SignUtils.sha256Hex(signingKey, canonicalRequest);

        // 添加签名到Header
        String signatureHeaderValue = authString + "/" + signedHeaders + "/" + signature;
        headers.put(SIGNATURE_HEADER_NAME, signatureHeaderValue);
    }

    /**
     * 生成规范URI路径
     */
    private String getCanonicalURIPath(String path) {
        if (path == null) {
            return "/";
        } else if (path.startsWith("/")) {
            return SignUtils.normalizePath(path);
        } else {
            return "/" + SignUtils.normalizePath(path);
        }
    }

    /**
     * 生成规范查询字符串
     */
    private static String getCanonicalQueryString(String queryStr) {
        if (queryStr == null || queryStr.isEmpty()) {
            return "";
        }

        List<String> queryStrings = new ArrayList<>();
        for (String pair : queryStr.split("&")) {
            String[] kv = pair.split("=", 2);
            String key = SignUtils.normalize(kv[0]);
            String value = kv.length == 2 ? SignUtils.normalize(kv[1]) : "";
            queryStrings.add(key + "=" + value);
        }

        Collections.sort(queryStrings);
        return SignUtils.join(QUERY_STRING_JOINER, queryStrings);
    }

    /**
     * 获取需要签名的Header
     */
    private SortedMap<String, String> getHeadersToSign(Map<String, String> headers, Set<String> headersToSign) {
        SortedMap<String, String> ret = new TreeMap<>();
        Set<String> targetHeaders = new HashSet<>();

        if (headersToSign != null) {
            targetHeaders.addAll(headersToSign.stream()
                    .map(String::trim)
                    .map(String::toLowerCase)
                    .collect(Collectors.toList()));
        }

        for (Map.Entry<String, String> entry : headers.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();

            if (value == null || value.isEmpty()) {
                continue;
            }

            String lowerKey = key.trim().toLowerCase();
            boolean isDefaultHeader = headersToSign == null &&
                    (lowerKey.startsWith(BCE_PREFIX_LOWER_CASE) || DEFAULT_HEADERS_TO_SIGN.contains(lowerKey));
            boolean isTargetHeader = headersToSign != null && targetHeaders.contains(lowerKey) &&
                    !SIGNATURE_HEADER_NAME.equalsIgnoreCase(key);

            if (isDefaultHeader || isTargetHeader) {
                ret.put(key, value);
            }
        }
        return ret;
    }

    /**
     * 生成规范Header字符串
     */
    private String getCanonicalHeaders(SortedMap<String, String> headers) {
        if (headers.isEmpty()) {
            return "";
        }

        List<String> headerStrings = new ArrayList<>();
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            String key = entry.getKey().trim().toLowerCase();
            String value = entry.getValue().trim();
            if (!value.isEmpty()) {
                headerStrings.add(SignUtils.normalize(key) + ":" + SignUtils.normalize(value));
            }
        }

        Collections.sort(headerStrings);
        return SignUtils.join(HEADER_JOINER, headerStrings);
    }

    // ====================== 内部辅助类 ======================

    /**
     * BCE授权信息类
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    private static class Credential {
        private String accessKey;
        private String secretKey;
    }

    /**
     * 签名选项类
     */
    @Data
    public static class SignOptions {
        public static final SignOptions DEFAULT = new SignOptions();
        private String timestamp;
        private int expirationInSeconds = 1800;
        private Set<String> headersToSign;
    }

    /**
     * 签名工具类
     */
    private static class SignUtils {
        private static final BitSet URI_UNRESERVED_CHARACTERS = new BitSet();
        private static final String[] PERCENT_ENCODED_STRINGS = new String[256];
        private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();

        static {
            // 初始化URI非保留字符
            for (int i = 'a'; i <= 'z'; i++) URI_UNRESERVED_CHARACTERS.set(i);
            for (int i = 'A'; i <= 'Z'; i++) URI_UNRESERVED_CHARACTERS.set(i);
            for (int i = '0'; i <= '9'; i++) URI_UNRESERVED_CHARACTERS.set(i);
            URI_UNRESERVED_CHARACTERS.set('-');
            URI_UNRESERVED_CHARACTERS.set('.');
            URI_UNRESERVED_CHARACTERS.set('_');
            URI_UNRESERVED_CHARACTERS.set('~');

            // 初始化百分比编码字符串
            for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; i++) {
                PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i);
            }
        }

        public static String join(String joinerStr, Iterable<String> iterable) {
            StringJoiner joiner = new StringJoiner(joinerStr);
            iterable.forEach(joiner::add);
            return joiner.toString();
        }

        public static String normalizePath(String path) {
            return normalize(path).replace("%2F", "/");
        }

        public static String normalize(String value) {
            StringBuilder builder = new StringBuilder();
            for (byte b : value.getBytes(StandardCharsets.UTF_8)) {
                if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
                    builder.append((char) b);
                } else {
                    builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]);
                }
            }
            return builder.toString();
        }

        public static String sha256Hex(String signingKey, String stringToSign) {
            try {
                Mac mac = Mac.getInstance("HmacSHA256");
                mac.init(new SecretKeySpec(signingKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
                byte[] signatureBytes = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
                return Hex.encodeHexString(signatureBytes);
            } catch (Exception e) {
                throw new RuntimeException("生成签名失败", e);
            }
        }
    }

    /**
     * 组织机构接口请求参数
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    private static class OrgRequestParam {
        private Integer pageSize;
        private Integer pageNum;
        private String userCode;
        private String authCode;
        //private String orgId;
    }

    /**
     * 员工数据接口请求参数
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    private static class EmpRequestParam {
        private Integer pageSize;
        private Integer pageNum;
        private String userCode;
        private String authCode;
        private String orgId;
    }

    /**
     * 通用分页响应基类
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class YthPageResponse<T> {
        private PageResult<T> result;

        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public static class PageResult<T> {
            private Integer pageSize;
            private Integer pageNum;
            private Integer totalNum;
            private List<T> result;
        }
    }

    /**
     * 组织机构数据模型
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class OrgData {
        private String id;
        private String name;
        private String parent_id;
        private String type;
        private String path_list;
    }

    /**
     * 员工数据模型
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class EmpData {
        private String id;
        private String code;
        private String name;
        private String mobile;
        private String org_id;
        private String org_parent_id;
        private String org_name;
        private String org_path_list;
        private String org_type;
        private String post_id;
        private String post_name;
    }
}

